package pers.chao.cacheable.core;

import pers.chao.cacheable.core.annotation.CacheMap;
import pers.chao.cacheable.infrastructure.ErrorDesc;
import pers.chao.cacheable.infrastructure.utils.RedisOp;
import pers.chao.tool.infrastructure.FuncInterface;
import pers.chao.tool.validate.ObjValidator;

import java.util.Map;
import java.util.Set;

/**
 * Description: map缓存操作
 *
 * @author W.Chao
 * @date 2020/10/2 14:33
 **/
public class CacheMapProcessor extends AbstractCacheProcessor {
	private CacheMap annotation;
	// 是否支持删除hash中的单个item，不为""时将删除指定item
	private String evictItem;
	// 是否支持添加或更新hash中的单个item，将返回值或返回值map中key(不是缓存的finalKey)与item相等的value更新到缓存map中
	private String updateItem;
	// 二者只允许使用其一
	private boolean evictItemIsNotEmpty;
	private boolean updateItemIsNotEmpty;

	private static final FuncInterface.T2R<Set, Boolean> TYPE_ASSERT = (set) -> {
		for (Object o : set) {
			return o instanceof String;
		}
		return false;
	};

	public CacheMapProcessor(CacheMap annotation) {
		this.annotation = annotation;
		this.evictItem = annotation.evictItem();
		this.updateItem = annotation.updateItem();
		this.evictItemIsNotEmpty = ObjValidator.isNotEmptyString(evictItem);
		this.updateItemIsNotEmpty = ObjValidator.isNotEmptyString(updateItem);
	}

	@Override
	protected void preInfo() {
		logger.info("[Cache map previous INFO], @cache -> {}, @cacheMap -> {}", cacheConditionInfo(), annotation);
	}

	@Override
	public void parseRedisStructKeyOrOther() {
		if (evictItemIsNotEmpty && updateItemIsNotEmpty) {
			throw new CacheableException(ErrorDesc.MAP_EVICT_ERROR);
		}
		if (evictItemIsNotEmpty) {
			this.evictItem = objectKeyParse(evictItem, evictItem);
		} else if (updateItemIsNotEmpty) {
			this.updateItem = objectKeyParse(updateItem, updateItem);
		}
	}

	@Override
	public Object preQuery() {
		if (RedisOp.hasKey(finalKey)) {
			return RedisOp.hmget(finalKey);
		}
		return null;
	}

	@Override
	@SuppressWarnings("all")
	public void afterMethodOperate(Object o) {
		if (evict) {
			RedisOp.del(finalKey);
			return;
		}

		// 删除指定item
		if (evictItemIsNotEmpty) {
			RedisOp.hdel(finalKey, evictItem);
			return;
		}

		// 判断是否是第一次添加该大key
		boolean isFirst = !RedisOp.hasKey(finalKey);

		if (o instanceof Map) {
			// 只支持string作为item的key
			Map<Object, Object> tmp = (Map<Object, Object>) o;
			Boolean typeAssert = TYPE_ASSERT.apply(tmp.keySet());
			if (!typeAssert) {
				throw new CacheableException(ErrorDesc.MAP_KEY_TYPE_ERROR, tmp.keySet().getClass());
			}

			Map<String, Object> convert = (Map<String, Object>) o;

			// 局部更新指定item
			if (updateItemIsNotEmpty && convert.containsKey(updateItem)) {
				RedisOp.hset(finalKey, updateItem, convert.get(updateItem));
				return;
			}

			// 不指定item情况下为全更新，已存在item为覆盖，无存在item则新增
			if (isFirst && isNeedExpire) {
				RedisOp.hmset(finalKey, convert, expire, unit);
			} else {
				RedisOp.hmset(finalKey, convert);
			}
		} else {
			// 将返回值缓存到指定item
			if (updateItemIsNotEmpty) {
				RedisOp.hset(finalKey, updateItem, o);
			}
		}
	}
}
